﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Text;

namespace ExpressesClass.BestApi.Utils.jsonUtil
{
    class JSONWriter
    {
        private StringBuilder buf = new StringBuilder();
        private Stack<Object> calls = new Stack<Object>();
        private Boolean emitClassName = true;

        public JSONWriter(Boolean emitClassName)
        {
            this.emitClassName = emitClassName;
        }

        public JSONWriter()
        {
            new JSONWriter(false);
        }

        public string write(Object obj)
        {
            buf.Length = 0;
            value(obj);
            return buf.ToString();
        }

        private void value(Object obj)
        {
            if (obj == null || cyclic(obj))
            {
                add(null);
            }
            else
            {
                calls.Push(obj);
                if (obj.GetType() == typeof(object)) stringHan(obj);
            else if (obj.GetType() == typeof(Boolean)) boolHan((Boolean)obj);            
            else if (obj.GetType() == typeof(string)) stringHan(obj);
			else if (IsNumberic(Convert.ToString(obj))) add(obj);
            else if (obj.GetType() == typeof(char)) stringHan(obj);
            else if (typeof(IList).IsAssignableFrom(obj.GetType())) arrayHan(obj);
            else if (obj.GetType() == typeof(DateTime)) dateHan((DateTime)obj);
            else if (obj.GetType() == typeof(Double) || obj.GetType() == typeof(float?) || obj.GetType() == typeof(long?)) add(obj);
            else bean(obj);
                calls.Pop();
            }
        }

        private void boolHan(Boolean b)
        {
            add(b? "true" : "false");
        }

        private void arrayHan(Object obj)
        {
            add("[");
            Type genericType = obj.GetType().GetGenericArguments()[0];
            int length = Convert.ToInt32(obj.GetType().GetProperty("Count").GetValue(obj, null));

            if (genericType.IsValueType)
            {
                for (int i = 0; i < length; ++i)
                {
                    object item = obj.GetType().GetProperty("Item").GetValue(obj, new object[] { i });
                    value(item);
                    if (i < length - 1) add(',');
                }
            }
            else
            {
                for (int i = 0; i < length; ++i)
                {
                    object item = obj.GetType().GetProperty("Item").GetValue(obj, new object[] { i });
                    value(item);
                    if (i < length - 1) add(',');
                }
            }
            add("]");
        }

        private void dateHan(DateTime date)
        {
            string format = "u";
            add("\"");
            add(date.ToString(format, DateTimeFormatInfo.InvariantInfo));
            add("\"");
        }

        private void stringHan(Object obj)
        {
            add('"');
            add(obj);
            add('"');
        }

        //copy from java api
        public Boolean isISOControl(int codePoint)
        {
            return codePoint <= 0x9F &&
                (codePoint >= 0x7F || (rightMove(codePoint, 5) == 0));
        }

        private int rightMove(int value, int pos)
        {

            if (pos != 0)  //移动 0 位时直接返回原值
            {
                int mask = 0x7fffffff;     // int.MaxValue = 0x7FFFFFFF 整数最大值
                value >>= 1;     //第一次做右移，把符号也算上，无符号整数最高位不表示正负
                                //但操作数还是有符号的，有符号数右移1位，正数时高位补0，负数时高位补1
                value &= mask;     //和整数最大值进行逻辑与运算，运算后的结果为忽略表示正负值的最高位
                value >>= pos - 1;     //逻辑运算后的值无符号，对无符号的值直接做右移运算，计算剩下的位
            }
            return value;
        }

        private void unicode(char c)
        {
            char[] hex = "0123456789ABCDEF".ToCharArray();
            add("\\u");
            int n = c;
            for (int i = 0; i < 4; ++i)
            {
                int digit = (n & 0xf000) >> 12;
                add(hex[digit]);
                n <<= 4;
            }
        }


        private void add(string name, Object obj)
        {
            add('"');
            add(name);
            add("\":");
            value(obj);
        }

        private void bean(Object obj)
        {
            add("{");
            Boolean addedSomething = false;

            PropertyInfo[] properties = obj.GetType().GetProperties();

            foreach (PropertyInfo item in properties)
            {
                string name = item.Name;
                object propertyValue = item.GetValue(obj);
                if (propertyValue == null) continue;
                if (addedSomething) add(',');
                add(name, propertyValue);
                addedSomething = true;
            }
             add("}");
        }



        private Boolean cyclic(Object obj)
        {

            foreach (Object ob in calls)
            {
                return ob == obj;
            }

            return false;
        }

        private void add(Object obj)
        {
            buf.Append(obj);
        }

        public string write(long n)
        {
            return Convert.ToString(n);
        }

        public string write(double d)
        {
            return Convert.ToString(d);
        }

        public string write(char c)
        {
            return "\"" + c + "\"";
        }

        private bool IsNumberic(string oText)
        {
            try
            {
                int var1 = Convert.ToInt32(oText);
                return true;
            }
            catch
            {
                return false;
            }
        }

    }
}
